package cn.lingyangwl.agile.auth.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.collection.CollUtil;
import cn.lingyangwl.agile.auth.assembly.ExtLdpAssembly;
import cn.lingyangwl.agile.auth.assembly.ExtLdpConnectAssembly;
import cn.lingyangwl.agile.auth.manager.ExtLdpConnectManager;
import cn.lingyangwl.agile.auth.mapper.ExtLdpMapper;
import cn.lingyangwl.agile.auth.model.entity.ExtLdp;
import cn.lingyangwl.agile.auth.model.entity.ExtLdpConnect;
import cn.lingyangwl.agile.auth.model.rqrs.ldp.*;
import cn.lingyangwl.agile.auth.service.ExtLdpService;
import cn.lingyangwl.agile.common.core.exception.enums.GlobalErrorEnum;
import cn.lingyangwl.agile.model.BasePlusPO;
import cn.lingyangwl.agile.model.ExcelBaseModel;
import cn.lingyangwl.agile.model.constants.*;
import cn.lingyangwl.agile.tenant.core.Tenant;
import cn.lingyangwl.framework.core.utils.spring.SpringUtils;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.exception.ArgumentNotValidException;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import cn.lingyangwl.framework.validation.ValidationUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 外部身份源模板
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class ExtLdpServiceImpl extends ServiceImpl<ExtLdpMapper, ExtLdp>
    implements IService<ExtLdp>, ExtLdpService {

    @Resource
    private ExtLdpConnectManager connectManager;
    @Resource
    private ExtLdpConnectAssembly ldpConnectAssembly;
    @Resource
    private ExtLdpAssembly extLdpAssembly;
    
    @Override
    @Tenant(level = DataIsolateLevel.MGT)
    public IPage<ExtLdpResp> pageExtLdp(ExtLdpPageReq req) {
        LambdaQueryWrapper<ExtLdp> lqw = ExtLdp.lqw()
            .eq(StringUtils.isNotEmpty(req.getType()), ExtLdp::getType, req.getType())
            .like(StringUtils.isNotEmpty(req.getName()), ExtLdp::getName, req.getName())
            .orderByDesc(ExtLdp::getCreatedAt);
        IPage<ExtLdp> pageData = this.page(PageUtil.to(req), lqw);
        return PageUtil.to(pageData, extLdpAssembly::toExtLdpResp);
    }
    
    @Override
    @Tenant(level = DataIsolateLevel.MGT)
    public List<ExtLdpResp> listExtLdp(ExtLdpListReq req) {
        LambdaQueryWrapper<ExtLdp> lqw = ExtLdp.lqw()
                .like(StringUtils.isNotEmpty(req.getName()), ExtLdp::getName, req.getName())
                .orderByDesc(BasePlusPO::getCreatedAt);
        return this.list(lqw).stream().map(extLdpAssembly::toExtLdpResp).collect(Collectors.toList());
    }

    @Override
    @Tenant(level = DataIsolateLevel.MGT)
    public ExtLdpResp getExtLdp(String code) {
        ExtLdp entity = this.getOne(ExtLdp.lqw().eq(ExtLdp::getCode, code));
        if (Objects.isNull(entity)) {
            throw new BizException(GlobalErrorEnum.OPERATION_FAIL_SELETE);
        }

        // 获取模板
        List<ExtLdpConnect> connections = connectManager.listByLdpId(entity.getId());
        ExtLdpResp resp = extLdpAssembly.toExtLdpResp(entity);
        
        List<ExtLdpConnectResp> connectionRespList = connections.stream()
                .map(ldpConnectAssembly::toConnectResp)
                .collect(Collectors.toList());

        resp.setConnections(connectionRespList);
        return resp;
    }

    @Override
    public boolean saveExtLdp(ExtLdpSaveReq req) {
        long count = this.count(ExtLdp.lqw().eq(ExtLdp::getCode, req.getCode()));
        if (count > 0) {
            throw new BizException("身份源已存在");
        }
        ExtLdp entity = extLdpAssembly.toExtLdp(req);
    	return this.save(entity);
    }

    @Override
    public boolean updateExtLdp(ExtLdpUpdateReq req) {
        long count = this.count(ExtLdp.lqw().eq(ExtLdp::getCode, req.getCode()).ne(ExtLdp::getId, req.getId()));
        if (count > 0) {
            throw new BizException("身份源已存在");
        }
        ExtLdp entity = extLdpAssembly.toExtLdp(req);
    	return this.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteExtLdp(Long id) {
        this.connectManager.deleteByLdpIds(Collections.singletonList(id));
    	return this.removeById(id);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteExtLdp(List<Long> ids) {
        this.connectManager.deleteByLdpIds(ids);
    	return this.removeByIds(ids);
    }

    @Override
    public List<ExtLdpExcel> exportExtLdp() {
        List<ExtLdp> ldpList = this.list();
        List<ExtLdpConnect> connectList = this.connectManager.list();
        Map<Long, List<ExtLdpConnect>> connectMap = connectList.stream().collect(Collectors.groupingBy(ExtLdpConnect::getLdpId));

        return ldpList.stream().map(extLdpAssembly::toLdpExcel).peek(e -> {
            List<ExtLdpExcel.Connect> connects = connectMap.getOrDefault(e.getId(), Collections.emptyList())
                .stream().map(ldpConnectAssembly::toConnectExcel).collect(Collectors.toList());
            e.setConnectList(connects);
        }).collect(Collectors.toList());
    }

    @Override
    public void importExtLdp(MultipartFile file) throws Exception {
        ImportParams params = new ImportParams();
        params.setTitleRows(0);
        params.setHeadRows(2);
        params.setNeedVerify(true);

        ExcelImportResult<ExtLdpExcel> importResult = ExcelImportUtil.importExcelMore(file.getInputStream(), ExtLdpExcel.class, params);
        if (Objects.isNull(importResult)) {
            log.warn("导入结果为空");
            return;
        }

        StringBuilder errorMsg = new StringBuilder();
        if (CollUtil.isNotEmpty(importResult.getFailList())) {
            for (ExcelBaseModel entity : importResult.getFailList()) {
                int line = entity.getRowNum() + 1;
                errorMsg.append(String.format("第 %s 行：%s",  line, entity.getErrorMsg())).append("<br/>");
            }
            throw new BizException(GlobalErrorEnum.EXCEL_IMPORT_FAIL, errorMsg.toString());
        }

        Map<String, ExtLdp> ldpMap = this.list().stream().collect(Collectors.toMap(ExtLdp::getCode, Function.identity()));
        Map<String, ExtLdpConnect> connectMap =  this.connectManager.list().stream()
            .collect(Collectors.toMap(ExtLdpConnect::getCode, Function.identity()));

        int count = 0;
        List<ExtLdpExcel> list = importResult.getList();
        list.forEach(ExtLdpExcel::removeEmptyConnect);
        List<ExtLdp> ldpList = new ArrayList<>(list.size());
        List<ExtLdpConnect> connectList = new ArrayList<>();

        try {
            for (ExtLdpExcel item : list) {
                item.setId(YitIdHelper.nextId());
                ldpList.add(extLdpAssembly.toLdp(item));
                count++;
                ValidationUtils.validateFastThrow(item);
                List<ExtLdpConnect> importConnect = item.getConnectList().stream()
                    .filter(e -> !connectMap.containsKey(e.getCode()))
                    .map(ldpConnectAssembly::toConnect).peek(e -> e.setLdpId(item.getId()))
                    .collect(Collectors.toList());
                connectList.addAll(importConnect);
                if (ldpMap.containsKey(item.getCode())) {
                    errorMsg.append(String.format("第 %s 行：身份源 [%s] 已存在",  count, item.getCode())).append("<br/>");
                }
            }
        } catch (ArgumentNotValidException e) {
            errorMsg.append(String.format("第 %s 行；%s",  count, e.getMessage())).append("<br/>");
        }
        String errorMsgStr = errorMsg.toString();
        if (StringUtils.isNotEmpty(errorMsgStr)) {
            throw new BizException(GlobalErrorEnum.EXCEL_IMPORT_FAIL, errorMsgStr);
        }

        SpringUtils.getAopProxy(this).doSaveImportExtLdp(ldpList, connectList);
    }

    @Transactional(rollbackFor = Exception.class)
    public void doSaveImportExtLdp(List<ExtLdp> ldpList, List<ExtLdpConnect> connectList) {
        this.saveBatch(ldpList);
        this.connectManager.saveBatch(connectList);
    }
}
